using System;
using System.Collections.Generic;
using System.Text;

namespace XiVo
{
  enum CommandNumberEnum : byte
  {
    powerUp = 0x00,
    powerDown = 0x01,
    getRadioID = 0x31,
    setMute = 0x13,
    changeChannel = 0x10, // first data byte is 02,
    getChannelInfo = 0x25,
    getExtendedChannelInfo = 0x22,
    getSignalData = 0x43,
    monitorChannelData = 0x50,
    modeListen = 0x74
  }

  enum ResponseNumberEnum : byte
  {
    powerUp = 0x80,
    powerDown = 0x81,
    getRadioID = 0xB1,
    setMute = 0x93,
    changeChannel = 0x90,
    getChannelInfo = 0xA5,
    getExtendedChannelInfo = 0xA2,
    getSignalData = 0xC3,
    monitorChannelData = 0xD0,
    channelNameChange = 0xd1,
    channelGenreChange = 0xd2,
    channelArtistTitleChange = 0xd3,
    channelSongTime = 0xd6,
    listenMode = 0xF5,
    radioOff = 0xff
  }

  class channelInfo
  {
    public byte channel;
    public string channelStr
    {
      get { return channel.ToString(); }
    }
    public string station;
    public string genre;
    public string artist;
    public string title;
  }

  class ResponseEventArgs : EventArgs
  {
    public ResponseNumberEnum responseNumber;
    public channelInfo channelInfo;
  }

  class Response : Object
  {
    Random rand = new Random();
    ASCIIEncoding enc = new ASCIIEncoding();
    public delegate void responseReceived(Response sender, ResponseEventArgs e);
    public event responseReceived responseReceivedEvent;
    public volatile bool done = false;
    public byte[] header = new byte[2];
    public byte[] length = new byte[2];
    public byte[] data;
    internal System.IO.Ports.SerialPort port;
    public System.Threading.Thread readDataThread;

    public Response(System.IO.Ports.SerialPort port)
    {
      this.port = port;
      data = new byte[port.ReadBufferSize];
      port.DiscardInBuffer();

      //readDataThread = new System.Threading.Thread(WaitForData);
      //readDataThread.Start();
    }

    public void poll()
    {
      while (port.BytesToRead > 0)
      {
        int len = readData();
        if (len > 0)
          handleResponse();
      }
    }

    public void WaitForData()
    {
      System.Diagnostics.Debug.WriteLine("Serial worker thread starting");
      int len;
      while (!done)
      {
        len = readData();
        if (len > 0)
          handleResponse();
      }
      System.Diagnostics.Debug.WriteLine("Serial worker thread complete.");
    }

    void wait(int length)
    {
      while (port.BytesToRead < length && done == false)
        System.Threading.Thread.Sleep(50);
    }

    int readData()
    {
      int len = 0;
      wait(2);
      port.Read(header, 0, 2);
      if (!(header[0] == 0x5a && header[1] == 0xa5))
        throw new Exception("Data sync error");
      wait(2);
      port.Read(length, 0, 2);
      len = length[0] * 256 + length[1] + 2;
      wait(len);
      port.Read(data, 0, len);
      return len;
    }

    private void handleResponse()
    {
      ResponseEventArgs e = new ResponseEventArgs();
      e.responseNumber = (ResponseNumberEnum)data[0];
      switch (e.responseNumber)
      {
        case ResponseNumberEnum.powerUp:
          break;
        case ResponseNumberEnum.powerDown:
          break;
        case ResponseNumberEnum.getChannelInfo:
          e.channelInfo = readChannelInfo();
          break;
        case ResponseNumberEnum.getExtendedChannelInfo:
          e.channelInfo = readExtChannelInfo();
          break;
        case ResponseNumberEnum.changeChannel:
          e.channelInfo = new channelInfo();
          e.channelInfo.channel = data[4];
          break;
        case ResponseNumberEnum.channelArtistTitleChange:
          e.channelInfo = ArtistTitleChanged();
          break;
        default:
          //System.Diagnostics.Debug.WriteLine("Response not handled: " + data[0].ToString("X"));
          //displayBuffer(">", data);
          break;
      }
      if (responseReceivedEvent != null)
      {
        responseReceivedEvent(this, e);
      }
    }

    channelInfo readChannelInfo()
    {
      channelInfo i = new channelInfo();
      i.channel = data[3];
      i.station = trimNull(enc.GetString(data, 6, 16));
      i.genre = trimNull(enc.GetString(data, 24, 16));
      i.artist = trimNull(enc.GetString(data, 41, 14));
      i.title = trimNull(enc.GetString(data, 57, 16));
      return i;
    }

    channelInfo readExtChannelInfo()
    {
      channelInfo i = new channelInfo();
      i.channel = 0;
      i.artist = trimNull(enc.GetString(data, 5, 32));
      i.title = trimNull(enc.GetString(data, 38, 35));
      return i;
    }

    channelInfo ArtistTitleChanged()
    {
      channelInfo i = new channelInfo();
      i.channel = data[1];
      i.station = "";
      i.genre = "";
      i.artist = trimNull(enc.GetString(data, 3, 16));
      i.title = trimNull(enc.GetString(data, 19, 16));
      return i;
    }

    String trimNull(String text)
    {
      return text.TrimEnd('\0').Trim();
    }

    public void displayBuffer(string prefix, byte[] buffer)
    {
      if (buffer != null)
      {
        int len = buffer.Length;
        System.Diagnostics.Debug.Write(prefix + " ");
        for (int i = 0; i < len; ++i)
        {
          System.Diagnostics.Debug.Write(buffer[i].ToString("X2"));
          System.Diagnostics.Debug.Write(' ');
        }
        System.Diagnostics.Debug.WriteLine("");
      }
    }

  }

}
